// Copyright (C) 2007 Gameloft. All rights reserved.
// GAMELOFT PROPRIETARY/CONFIDENTIAL.
//
// Author: balife

#include "../PreHeader.h"

#include "texture.h"
#include "Debug.h"
#include "Display.h"
#include "Memory.h"



// RUIZD: Uncomment if necessary
#include "Game.h"

// RUIZD: Check it later
//#ifdef __M25__
#include "Sprite_Indics.h"
//#include "spriteslist.h"
//#elif defined __M1A__
//#include "../imagelist_m1a.h"
//#endif

#define SII_ALPHA		1
#define SII_PAL8888		3
#define SII_4444		5
#define SII_565			8
#define SII_5551		11
#define SII_8888		13

CustomShaderInfo gGLShaders[] = 
{
	// Non-textured
	{
		0						   // mTextureType
		,IPL_BSV_UNIFORMBLENDING   // IPL_BSV_UNIFORMBLENDINGFP
		,isUniformColor            // mbUniform
		,0                         // mTexture0Format
		,0
	},
	
	{
		ALPHA
		,IPL_BSV_A8NEARESTMODWITHUNIFORMBLENDING//IPL_BSV_A8NEARESTMODWITHUNIFORMBLENDINGFP //IPL_BSV_A8LINEARMODWITHUNIFORMBLENDINGFP
		,isUniformColor                  // mbUniform
		,GL_ALPHA					 // mTexture0Type
		,GL_UNSIGNED_BYTE          // mTexture0Format		
	},

	{
		ALPHA
		,IPL_BSV_A8LINEARMODWITHUNIFORMBLENDING//IPL_BSV_A8LINEARMODWITHUNIFORMBLENDINGFP //
		,(isUniformColor|isLiner)                  // mbUniform
		,GL_ALPHA					 // mTexture0Type
		,GL_UNSIGNED_BYTE          // mTexture0Format
	},
		
	{
		(PAL256|RGBA8888)
		,IPL_BSV_P8_8888NEARESTBLENDING//IPL_BSV_P8_8888NEARESTBLENDINGFP
		,isNone                  // mbUniform
		,GL_PALETTE8_RGBA8_OES		// mTexture0Type
		,GL_UNSIGNED_BYTE          // mTexture0Format		
	},

	{
		(PAL256|RGBA8888)
		,IPL_BSV_P8_8888LINEARBLENDING
		,isLiner                  // mbUniform
		,GL_PALETTE8_RGBA8_OES		// mTexture0Type
		,GL_UNSIGNED_BYTE          // mTexture0Format
	},	

	// 4444
	{
		RGBA4444
		,IPL_BSV_4444NEARESTBLENDING
		,isNone                  // mbUniform		
		,GL_RGBA                   // mTexture0Format
		,GL_UNSIGNED_SHORT_4_4_4_4 // mTexture0Type
	},
	
	{
		RGBA4444
		,IPL_BSV_4444LINEARBLENDING
		,isLiner                  // mbUniform
		,GL_RGBA                   // mTexture0Format
		,GL_UNSIGNED_SHORT_4_4_4_4 // mTexture0Type
	},
	
	{
		RGBA4444
		,IPL_BSV_4444LINEARMODWITHUNIFORMBLENDING//IPL_BSV_4444LINEARMODWITHUNIFORMFP
		,(isUniformColor|isLiner)                   // mbUniform
		,GL_RGBA                   // mTexture0Format
		,GL_UNSIGNED_SHORT_4_4_4_4 // mTexture0Type
	},

	// 565
	{
		RGB565
		,IPL_BSV_565NEAREST
		,isNone                  // mbUniform
		,GL_RGB                    // mTexture0Format
		,GL_UNSIGNED_SHORT_5_6_5   // mTexture0Type
	},

	{
		RGB565		
		,IPL_BSV_565LINEAR
		,isLiner                  // mbUniform
		,GL_RGB                    // mTexture0Format
		,GL_UNSIGNED_SHORT_5_6_5   // mTexture0Type
	},
	
	{
		RGB565
		,IPL_BSV_565LINEARMODWITHUNIFORM
		,(isUniformColor|isLiner)                   // mbUniform
		,GL_RGB                    // mTexture0Format
		,GL_UNSIGNED_SHORT_5_6_5   // mTexture0Type
	},

	//5551
	{
		RGBA5551
		,IPL_BSV_5551NEARESTBLENDING
		,isNone                  // mbUniform
		,GL_RGBA                   // mTexture0Format
		,GL_UNSIGNED_SHORT_5_5_5_1 // mTexture0Type
	},
	
	{
		RGBA5551		
		,IPL_BSV_5551LINEARBLENDING
		,isLiner                  // mbUniform
		,GL_RGBA                   // mTexture0Format
		,GL_UNSIGNED_SHORT_5_5_5_1 // mTexture0Type
	},
	
	// 8888
	{
		RGBA8888
		,IPL_BSV_8888NEARESTBLENDING//IPL_BSV_8888NEARESTFP
		,isNone                  // mbUniform
		,GL_RGBA                   // mTexture0Format
		,GL_UNSIGNED_BYTE          // mTexture0Type
	},
	
	{
		RGBA8888
		,IPL_BSV_8888LINEARBLENDING
		,isLiner                  // mbUniform
		,GL_RGBA                   // mTexture0Format
		,GL_UNSIGNED_BYTE          // mTexture0Type
	}
};

#define ShaderNum sizeof(gGLShaders) / sizeof(CustomShaderInfo)

void setStateVector(int id_)
{
	//g2DCommRI.ShaderInfoId = id_;
	int curId = id_;
	if( g2DCommRI.transform & (IS_ROT|IS_SCALE|IS_UNIFORMCOLOR) )
	{
		int i = id_;
		while( (i<ShaderNum) && (gGLShaders[id_].imgFmt == gGLShaders[i].imgFmt) )
		{
			if( (g2DCommRI.transform & (IS_ROT|IS_SCALE)) && (gGLShaders[i].Flag&isLiner) )
			{
				curId = i;
				//break;
			}
			if( gGLShaders[i].Flag&isUniformColor )
			{
				curId = i;
				break;
			}
			i++;
		}
	}
	IPLGLStateVector_SetCurrentToBuiltin( gGLShaders[curId].ShaderVector );

#ifdef WIN32
	
	// RUIZD: Find a way to replace the OpenGL ES function "glActiveTexture" with an OpenGL function.
	// TODO

#else // iPhone
	glActiveTexture(GL_TEXTURE0);
#endif
}
extern const char* kImageList[];
//------------- TextureManager -------------
TextureManager::TextureManager()
{
	m_texMaxNum = 0;
}

TextureManager::~TextureManager()
{
	SAFE_DELETE(m_texInfo);
}

bool TextureManager::Init()
{
	m_texMaxNum = SPRITE_NUM + 1;
	m_texInfo= GL_NEW TexInfo[m_texMaxNum];
	for(int i=0; i< m_texMaxNum; ++i)
	{
		m_texInfo[i].filename = kImageList[i];
		m_texInfo[i].name = -1;
		m_texInfo[i].width = 0;
		m_texInfo[i].height = 0;
		m_texInfo[i].ShaderInfoId = 0;
		m_texInfo[i].size = 0;
		m_texInfo[i].ref = 0;
#ifdef _USE_PALIMAGE_BUFFER
		m_texInfo[i].pData = NULL;
#endif
	}
	//Read("image.lst",this,(void*)-1);
	//{
	//	GLuint name  = -1;
	//	int _area = 320*240;
	//	int _size = 256*4+_area;
	//	unsigned char* tex_buff = GL_NEW unsigned char[_size];
	//	MEMSET( tex_buff, 0, _size);
	//	for(int i = 0; i < 44; i++ )
	//	{	
	//		glGenTextures( 1,&name );			
	//		glBindTexture( GL_TEXTURE_RECTANGLE_ARB, name );
	//		//glTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA, 320, 240, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL );
	//		
	//		glCompressedTexImage2D( GL_TEXTURE_RECTANGLE_ARB, 0, GL_PALETTE8_RGBA8_OES, 320, 240, 0, _size, tex_buff );
	//	}
	//}
	return true;
}

#define PAL_256LEN 256*4
//struct _TexEntry
//{
//	GLuint            mTexIndex;
//	GLuint            mTexName;
//	GLsizei           mSize;
//	GLsizei           mTexWidth;
//	GLsizei           mTexHeight;
//	GLsizei           mWidth;
//	GLsizei           mHeight;
//
//	GLboolean         mbCompressed;
//	GLint             mInternalFormat;
//	GLenum            mType;
//
//	struct _TexEntry* mpNextFree;
//};
//struct _GLState
//{
//	// Texture Names
//	_TexEntry  mTexEntries[64];
//};
//extern _GLState gGLState;
#ifdef _USE_RLE
const char * RLE_TGA_DecodeLine (const char * InBuffer, int iColorBit,
								  int iNumPixel, char * OutBuffer)
{
	register char	  Data ;
	register int	  Num ;
	iColorBit /= 8 ; //
	while (iNumPixel > 0)
	{
		Data = *InBuffer++ ; // Next Byte
		if (Data & 0x80) // Data >= 0x80
		{
			iNumPixel -= (Num = (Data & 0x7F) + 1) ;
			for (int i = 0 ; i < Num ; i++, OutBuffer += iColorBit)
				memcpy (OutBuffer, InBuffer, iColorBit) ;
			InBuffer += iColorBit ;
		}
		else //
		{
			iNumPixel -= (Num = ++Data) ;
			Num *= iColorBit ;
			memcpy (OutBuffer, InBuffer, Num) ;
			OutBuffer += Num ;
			InBuffer += Num ;
		}
	} // End of while
	return InBuffer ;
}
#endif
void TextureManager::GenTex(CMemoryStream *pFS, int texIdx)
{
	const char * buf = (char*)pFS->getData();
	const unsigned char * buf2 = (unsigned char*)pFS->getData();
	{
		m_texInfo[texIdx].size = pFS->getLength() - 5;
		byte texFormat = buf[0];
		int width  = (short)((buf[2] <<  8) | (buf[1] & 0xff));
		int height = (short)((buf[4] <<  8) | (buf[3] & 0xff));
		m_texInfo[texIdx].width  = width;
		m_texInfo[texIdx].height = height;
#ifdef WIN32
		unsigned char *buffer = NULL;
#endif
		//GLenum internalformat = GL_RGBA;
		//bool isCompressTex = false;
		int shaderInfoId = 0;
	/*	if( texFormat == (PAL256 | RGB565) )
		{
			m_texInfo[texIdx].type = GL_UNSIGNED_SHORT_5_6_5;
			m_texInfo[texIdx].stateVector = IPL_BSV_P8_565NEARESTFP;
			isCompressTex = true;
			internalformat = GL_PALETTE8_R5_G6_B5_OES;		
		}
		else */if( texFormat == (PAL256 | RGBA8888) )
		{
			//m_texInfo[texIdx].type = GL_UNSIGNED_BYTE;
			//m_texInfo[texIdx].stateVector = IPL_BSV_P8_8888NEARESTBLENDINGFP;
			//isCompressTex = true;
			//internalformat = GL_PALETTE8_RGBA8_OES;
			shaderInfoId = SII_PAL8888;
	#ifdef _USE_PALIMAGE_BUFFER
			m_texInfo[texIdx].pData = GL_NEW char [m_texInfo[texIdx].size];
			MEMCPY( m_texInfo[texIdx].pData, &buf[5], m_texInfo[texIdx].size );
			//printf("SII_PAL8888\n");
			return;
	#endif
		}
		/*else if( texFormat == (PAL16 | RGBA4444) )
		{		
			m_texInfo[texIdx].type = GL_UNSIGNED_SHORT_4_4_4_4;
			// RUIZD
			//m_texInfo[texIdx].stateVector = IPL_BSV_P4_4444LINEARMODWITHUNIFORMBLENDINGFP;
			isCompressTex = true;
			internalformat = GL_PALETTE4_RGBA4_OES;
		}
		else if( texFormat == (PAL256 | RGBA5551) ) //not support
		{		
			//m_texInfo[texIdx].type = GL_UNSIGNED_SHORT_5_5_5_1;
			// RUIZD
			//m_texInfo[texIdx].stateVector = -1;//IPL_BSV_5551NEARESTBLENDINGFP;
			//isCompressTex = true;
			//internalformat = GL_PALETTE8_RGB5_A1_OES;
		}*/
		else if( texFormat == RGBA4444 )
		{
			//m_texInfo[texIdx].type = GL_UNSIGNED_SHORT_4_4_4_4;
			// RUIZD
			//m_texInfo[texIdx].stateVector = IPL_BSV_4444NEARESTBLENDINGFP;//IPL_BSV_4444LINEARMODWITHUNIFORMBLENDINGFP;
#ifdef WIN32
			buffer = GL_NEW unsigned char[width*height*4];
			for (int i=0;i< width*height*2; i+= 2)
			{
				buffer[i*2 + 2] = (buf[5 + i+0] & 0xf0);
				buffer[i*2 + 3] = (buf[5 + i+0] & 0x0f) * 0xff / 0x0f;
				buffer[i*2 + 0] = (buf[5 + i+1] & 0xf0);
				buffer[i*2 + 1] = (buf[5 + i+1] & 0x0f) * 0xff / 0x0f;
			}
			shaderInfoId = SII_8888;
#else
			shaderInfoId = SII_4444;
#endif
			//printf("SII_4444\n");
		}
		else if( texFormat == RGBA5551 )
		{
			//m_texInfo[texIdx].type = GL_UNSIGNED_SHORT_5_5_5_1;
			//m_texInfo[texIdx].stateVector = IPL_BSV_5551NEARESTBLENDINGFP;//IPL_BSV_5551LINEARBLENDINGFP;
#ifdef WIN32
			buffer = GL_NEW unsigned char[width*height*4];
			ULONG px;
			for (int i=0;i< width*height*2; i+= 2)
			{
				px = ((buf[5 + i+1]<<8) | (buf[5 + i+0] & 0xff)) & 0xffff;
				int r = (px>>11) & 0x1F;
				int g = (px>>6) & 0x1F;
				int b = (px>>1) & 0x1F;
				int a = px & 0x1 ? 0xff: 0x0;
				buffer[i*2 + 2] = b * 0xff / 0x1f;
				buffer[i*2 + 3] = a;
				buffer[i*2 + 0] = r * 0xff / 0x1f;
				buffer[i*2 + 1] = g * 0xff / 0x1f;
			}
			shaderInfoId = SII_8888;
#else
			shaderInfoId = SII_5551;
#endif
			//printf("SII_5551\n");
		}
		else if( texFormat == RGB565 )
		{
			//m_texInfo[texIdx].type = GL_UNSIGNED_SHORT_5_6_5;
			//m_texInfo[texIdx].stateVector = IPL_BSV_565NEARESTFP;
			//m_texInfo[texIdx].stateVector = IPL_BSV_565LINEARMODWITHUNIFORMFP;
			//internalformat = GL_RGB;
#ifdef WIN32
			buffer = GL_NEW unsigned char[width*height*4];
			ULONG px;
			for (int i=0;i< width*height*2; i+= 2)
			{
				px = ((buf[5 + i+1]<<8) | (buf[5 + i+0] & 0xff)) & 0xffff;
				int r = (px>>11) & 0x1F;
				int g = (px>>5) & 0x3F;
				int b = (px) & 0x1F;
				int a = 0xff;
				buffer[i*2 + 2] = b * 0xff / 0x1f;
				buffer[i*2 + 3] = a;
				buffer[i*2 + 0] = r * 0xff / 0x1f;
				buffer[i*2 + 1] = g * 0xff / 0x3f;
			}
			shaderInfoId = SII_8888;
#else
			shaderInfoId = SII_565;
#endif
			//printf("SII_565\n");
		}
		else if( texFormat == RGBA8888 )
		{
			//m_texInfo[texIdx].type = GL_UNSIGNED_BYTE;
			//m_texInfo[texIdx].stateVector = IPL_BSV_8888NEARESTBLENDINGFP;//IPL_BSV_8888NEARESTFP;
			shaderInfoId = SII_8888;
			//printf("SII_8888\n");
		}
		else if( texFormat == ALPHA )
		{
			//m_texInfo[texIdx].type = GL_UNSIGNED_BYTE;
			//m_texInfo[texIdx].stateVector = IPL_BSV_A8NEARESTMODWITHUNIFORMBLENDINGFP;
			//internalformat = GL_ALPHA;
#ifdef WIN32
			buffer = GL_NEW unsigned char[width*height*4];
			for (int i=0;i< width*height; i++)
			{
				buffer[i*4 + 0] = 0xff;
				buffer[i*4 + 1] = 0xff;
				buffer[i*4 + 2] = 0xff;
				buffer[i*4 + 3] = buf[5 + i];
			}
			shaderInfoId = SII_8888;
#else
			shaderInfoId = SII_ALPHA;
#endif
			//printf("SII_ALPHA\n");
		}
	#ifdef _DEBUG
		else
			ASSERT(0);
	#endif

		m_texInfo[texIdx].ShaderInfoId = shaderInfoId;
		GLenum internalformat = gGLShaders[shaderInfoId].InternalFormat;
		GLenum type = gGLShaders[shaderInfoId].Type;

		m_texInfo[texIdx].name = GetIdleTexName( m_texInfo[texIdx].size );
		m_texInfo[texIdx].ref     = 1;

		// RUIZD: Changed GL_TEXTURE_RECTANGLE_ARB for GL_TEXTURE_2D
		glBindTexture( GL_TEXTURE_2D, m_texInfo[texIdx].name );
	

		if(texFormat&PAL256)
		{

	#ifdef WIN32
			// RUIZD: Implement in OpenGL
			// TODO
	#else // iPhone
			// RUIZD: Changed GL_TEXTURE_RECTANGLE_ARB for GL_TEXTURE_2D
			glCompressedTexImage2D( GL_TEXTURE_2D, 0, internalformat, width, height, 0, m_texInfo[texIdx].size, &buf[5] );
	#endif

		}
		else
		{
			// RUIZD: Changed GL_TEXTURE_RECTANGLE_ARB for GL_TEXTURE_2D
			//
			// PixelFormat:
			/* PixelFormat */
			// #define GL_ALPHA                          0x1906
			// #define GL_RGB                            0x1907
			// #define GL_RGBA                           0x1908
			// #define GL_LUMINANCE                      0x1909
			// #define GL_LUMINANCE_ALPHA                0x190A
			//
			// PixelStoreParameter
			// #define GL_UNPACK_ALIGNMENT               0x0CF5
			// #define GL_PACK_ALIGNMENT                 0x0D05
			//
			// PixelType
			// #define GL_UNSIGNED_SHORT_4_4_4_4         0x8033
			// #define GL_UNSIGNED_SHORT_5_5_5_1         0x8034
			// #define GL_UNSIGNED_SHORT_5_6_5           0x8363
			//
			// Type:
			// GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, and GL_FLOAT
#ifdef WIN32
			glTexImage2D( GL_TEXTURE_2D, 0, internalformat, width, height, 0, internalformat, type, buffer==NULL?&buf[5]:(char*)buffer );
			SAFE_DELETE(buffer);
#else
			glTexImage2D( GL_TEXTURE_2D, 0, internalformat, width, height, 0, internalformat, type, &buf[5]);
#endif
			//glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &buf[5] );
			//glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT, &buf[5] );

			glBindTexture(GL_TEXTURE_2D, m_texInfo[texIdx].name);
			
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
			
			int res = glGetError();
			if (res == GL_INVALID_VALUE)
			{
				int a = 1;
			}
		}
	}

		

}

int TextureManager::GetTexIndex(const char * filename)
{
	for( int i = 0; i < m_texMaxNum; i++ )
	{
		if( STRCMP(m_texInfo[i].filename, filename) == 0 )
				return i;
	}
	ASSERT(0);//not exist!
	return -1;
}

const char* LoadTexture(int texIdx)
{
	//const char* filename = getTexMgr()->m_texInfo[texIdx].filename;
	char filename[32];
	sprintf(filename,"data/%s.bbm",getTexMgr()->m_texInfo[texIdx].filename);
	if(getTexMgr()->m_texInfo[texIdx].ref == 0)
		Read(filename,getTexMgr(),(void*)texIdx);
	else
		getTexMgr()->m_texInfo[texIdx].ref++;
	//LOG_MSG("Tex|Load| %s|Ref| %d",m_texInfo[texIdx].filename,m_texInfo[texIdx].ref);
	return filename;
}

int LoadTexture(const char* filename)
{
	int texIdx = getTexMgr()->GetTexIndex(filename);
	LoadTexture(texIdx);
	return texIdx;
}

/*void TextureManager::LoadImageList(char* buf, int size)
{
}*/

extern int  getGameState();
void TextureManager::LoadFile(void* object, CFileStream *pFS)
{
	TextureManager* tm = (TextureManager*)object;
	CMemoryStream ms;
	int flag = (int)(pFS->mpUseData);
	ms.Attach(*pFS);
	if(flag == -1)
	{
		// RUIZD: Esta funciÛn existÌa pero todo su cÛdigo estaba comentado
		// tm->LoadImageList((char*)ms.getData(), ms.getLength());
	}
	else
	{
		tm->GenTex(&ms,flag);
		
		// LOG_MSG("State %d: Load %s [%d]-->[%d]",getGameState(),pFS->mFilename,flag, tm->m_texInfo[flag].name);
	}
}

void TextureManager::Release(int texIdx)
{
	ASSERT(texIdx >= 0 && texIdx< m_texMaxNum);
	if( --m_texInfo[texIdx].ref <= 0 )
	{
		m_texInfo[texIdx].ref = 0;
		Delete(texIdx);
	}
}

void TextureManager::Release(int pal[], int size)
{
	for(int i = 0; i < size; ++i)
	{
		Release(pal[i]);
	}
}
void TextureManager::Delete(int texIdx)
{
	//ASSERT(texIdx>=0 && texIdx <m_texMaxNum && m_texInfo[texIdx].ref == 0 && m_texInfo[texIdx].name == -1);
	if (m_texInfo[texIdx].name != -1)
	{
		glDeleteTextures(1, &m_texInfo[texIdx].name);
	}
	m_texInfo[texIdx].name = -1;
	m_texInfo[texIdx].width = 0;
	m_texInfo[texIdx].height = 0;
	m_texInfo[texIdx].size = 0;
	m_texInfo[texIdx].ref = 0;
#ifdef _USE_PALIMAGE_BUFFER
	SAFE_DELETE( m_texInfo[texIdx].pData ); //lr: 20070612
#endif
}

void TextureManager::Reset()
{
	for(int i=0; i< m_texMaxNum; ++i)
	{
		Delete(i);
	}

}

const TexInfo * TextureManager::GetTex(int texIdx)
{
	if( (texIdx < 0) || (texIdx >= m_texMaxNum) )
		return NULL;

	return &(m_texInfo[texIdx]);
}

GLuint TextureManager::GetIdleTexName(T_U32 size)
{
	GLuint name  = -1;	
	glGenTextures(1, &name);
	//bneedRestGL = true;
	return name;
}

